/*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
*
*
* Authors:                  zhoujian
* Email:            343600434@qq.com
* Date:         Jan-30-2024 10:48:08
*
*/


#include "FreeRTOS.h"
#include "gd32l23x.h"

#include "flash.h"
#include <string.h>
#include <stdio.h>
#include "gd32l23x_eval.h"
#include "gd32l23x_it.h"
#include "Caige.h"
#include "device.h"
#include "input.h"


FLASH_INFO flash_info;
FLASH_INFO flash_info_save;

//uint32_t id[3];
FLASH_T flash_st;

void unique_id(void)
{
    //id[0] = *(uint32_t*)ID_ADDR0;
    //id[1] = *(uint32_t*)ID_ADDR1;
    //id[2] = *(uint32_t*)ID_ADDR2;
}
static fmc_state_enum fmc_state_get(void)
{
    fmc_state_enum fmc_state = FMC_READY;

    if((uint32_t)0x00U != (FMC_STAT & FMC_STAT_BUSY)) {
        fmc_state = FMC_BUSY;

    } else {
        if((uint32_t)0x00U != (FMC_STAT & FMC_STAT_WPERR)) {
            fmc_state = FMC_WPERR;

        } else {
            if((uint32_t)0x00U != (FMC_STAT & (FMC_STAT_PGERR))) {
                fmc_state = FMC_PGERR;

            } else {
                if((uint32_t)0x00U != (FMC_STAT & (FMC_STAT_PGAERR))) {
                    fmc_state = FMC_PGAERR;
                }
            }
        }
    }

    /* return the FMC state */
    return fmc_state;
}

void flash_task(void)
{
    uint8_t flash_status = flash_st.status;
    //TEST_HIGH;

    switch(flash_status) {
        case FLASH_READY:
            if(FMC_READY == fmc_state_get()) {
                FMC_CTL |= FMC_CTL_PER;
                FMC_ADDR = SAVE_ADDR;
                FMC_CTL |= FMC_CTL_START;
                flash_st.status = FLASH_ERASE_READY;
                flash_st.timeout = 5;

            } else {
                if(flash_st.timeout) {
                    flash_st.timeout--;

                } else {
#ifdef DEBUG_BOOT_GET_FLASH
                    log_debug("FLASH_READY timeout\n");
#endif
                    //if(flash_st.timeout == 0) {
                    flash_st.status = FLASH_END;
                }
            }

            break;

        case FLASH_ERASE_READY:
            if(FMC_READY == fmc_state_get()) {
                flash_st.status = FLASH_ERASE_OK;
                FMC_CTL &= ~FMC_CTL_PER;

            } else {
                if(flash_st.timeout) {
                    flash_st.timeout--;

                } else {
#ifdef DEBUG_BOOT_GET_FLASH
                    log_debug("FLASH_ERASE_READY timeout\n");
#endif
                    //if(flash_st.timeout == 0) {
                    flash_st.status = FLASH_END;
                }
            }

        /* reset the PER bit */
        case FLASH_ERASE_OK://ok

            //log_debug("FMC_STAT=0x%08x\n", FMC_STAT);
            if((uint32_t)0x00U != (FMC_STAT & FMC_STAT_ENDF)) {
                FMC_STAT |= FMC_STAT_ENDF;
                fmc_lock();
                //if(flash_st.int_flag) {
                flash_st.int_flag = 0;
                flash_st.timeout = 5;
                flash_st.status = FLASH_WRITE_READY;
                //flash_st.status =FLASH_END;
                //}

            } else {
                if(flash_st.timeout) {
                    flash_st.timeout--;

                } else {
#ifdef DEBUG_BOOT_GET_FLASH
                    log_debug("FLASH_ERASE_OK timeout\n");
#endif
                    //if(flash_st.timeout == 0) {
                    flash_st.status = FLASH_END;
                }
            }

            break;

        case FLASH_WRITE_READY:
            if(FMC_READY == fmc_state_get()) {
                flash_st.status = FLASH_WRITE_READY2;
                uint32_t addr0 = SAVE_ADDR + ((flash_st.addr + flash_st.index) * 8);
                uint32_t v = *(flash_st.dat + flash_st.index);
                uint32_t addr1 = SAVE_ADDR + ((flash_st.addr + flash_st.index) * 8 + 4);
                uint32_t v1 = *(flash_st.dat + flash_st.index + 1);
#ifdef DEBUG_BOOT_GET_FLASH
                log_debug("w: 0x%08x: 0x%08x\n", addr0, v);
                log_debug("   : 0x%08x: 0x%08x\n", addr1, v1);
#endif
                fmc_unlock();
                FMC_CTL |= FMC_CTL_PG;
                REG32(addr0) = v;
                REG32(addr1) = v1;

            } else {
                if(flash_st.timeout) {
                    flash_st.timeout--;

                } else {
                    //if(flash_st.timeout == 0) {
#ifdef DEBUG_BOOT_GET_FLASH
                    log_debug("FLASH_WRITE_READY timeout\n");
#endif
                    flash_st.status = FLASH_END;
                }
            }

            break;

        case FLASH_WRITE_READY2:
            if(FMC_READY == fmc_state_get()) {
                FMC_CTL &= ~FMC_CTL_PG;
                flash_st.status = FLASH_WRITE_OK;

            } else {
                if(flash_st.timeout) {
                    flash_st.timeout--;

                } else {
#ifdef DEBUG_BOOT_GET_FLASH
                    log_debug("FLASH_WRITE_READY2 timeout\n");
#endif
                    //if(flash_st.timeout == 0) {
                    flash_st.status = FLASH_END;
                }
            }

            break;

        case FLASH_WRITE_OK:
            if((uint32_t)0x00U != (FMC_STAT & FMC_STAT_ENDF)) {
                FMC_STAT |= FMC_STAT_ENDF;
                //if(flash_st.int_flag) { //ok
                flash_st.int_flag = 0;
                flash_st.len--;

                if(flash_st.len) {
                    flash_st.timeout = 5;
                    flash_st.status = FLASH_WRITE_READY;
                    flash_st.index++;

                } else {
                    flash_st.status = FLASH_END;
#ifdef DEBUG_BOOT_GET_FLASH0
                    log_debug("\t0x%x\t0x%x\n\n", SAVE_ADDR + (((flash_st.addr + flash_st.index) * 8)), SAVE_ADDR + (((flash_st.addr + flash_st.index) * 8) + 4));

                    if(((flash_st.addr << 2) & 0xF) == 8) {
                        log_debug("\n");
                    }

#endif
                    flash_st.index++;
                }

            } else {
                if(flash_st.timeout) {
                    flash_st.timeout--;

                } else {
#ifdef DEBUG_BOOT_GET_FLASH
                    log_debug("FLASH_WRITE_OK timeout\n");
#endif
                    //if(flash_st.timeout == 0) {
                    flash_st.status = FLASH_END;
                }
            }

            break;

        case FLASH_END:
            fmc_interrupt_disable(FMC_INT_END);
            flash_st.status = FLASH_IDLE;
            fmc_lock();

        case FLASH_IDLE:
        default:
            break;
    }

    //TEST_LOW;
}

uint32_t flash_n = 0;

FlagStatus write_data_flash(uint32_t* value)
{
    FlagStatus fs = RESET;
    memcpy((uint8_t*)&flash_info_save, (uint8_t*)value, 8);

    if(flash_st.status == FLASH_IDLE) {
        //log_debug(" addr:0x%08x value:0x%08x\n", value, *value);
        flash_st.dat = (uint32_t*)&flash_info_save;
        flash_st.len = 1;
        fmc_unlock();
        //fmc_interrupt_enable(FMC_INT_ERR|FMC_INT_END);
        flash_st.int_flag = 0;
        flash_st.timeout = 5;
        flash_st.index = 0;

        if(flash_n == 0) {
            flash_st.status = FLASH_READY;
            flash_st.addr = 0;
#ifdef DEBUG_BOOT_GET_FLASH
            log_debug("erase\n");
#endif

        } else {
            flash_st.status = FLASH_WRITE_READY;
            flash_st.addr = flash_n;
        }

        flash_n += 1;
        flash_n &= (FLASH_PAGE_SIZE / 8 - 1);
        fs = SET;

    } else {
#ifdef DEBUG_BOOT_GET_FLASH
        log_debug("write busy !!! %d\n", motor_fault);
#endif
    }

    return fs;
}
uint32_t odo_km_v[2];
uint32_t boot_odo_km_v[2];

void trip_clean()
{
    range_info.trip = 0;

    if((flash_info.trip != 0) || (flash_info.trip_dam != 0)) {
        flash_info.trip = 0;
        flash_info.trip_dam = 0;
        write_data_flash((uint32_t*)&flash_info);
    }
}

void clear_odo_trip()
{
    range_info.odo = 0;
    range_info.mul = 0;
    range_info.trip = 0;
    flash_info.trip = 0;
    flash_info.odo_dam = 0;
    flash_info.trip_dam = 0;
    flash_info.odo = 0;
    flash_info.mul = 0;
    flash_info.sel = 0;
    //todo: save flash
    write_data_flash((uint32_t*)&flash_info);
}

void get_odo_from_flash(void)
{
    uint32_t i;
    uint32_t tmp;
    uint32_t tmp2;

    for(i = 0; i < FLASH_PAGE_SIZE / 8 ; i ++) {
        tmp = (*(uint32_t*)(SAVE_ADDR + i * 8));
        tmp2 = (*(uint32_t*)(SAVE_ADDR + i * 8 + 4));
        //range_info.odo = (tmp );
        //log_debug("read=0x%x 0x%x ", tmp,tmp2);
        //range_info.mul = (tmp>>16)&0xFF;
#ifdef DEBUG_BOOT_GET_FLASH

        if((i & 0xF) == 0) {
            log_debug("%d: ", i / 16);
        }

        log_debug("%08X\t%08X\t", tmp, tmp2);

        if((i & 0xF) == 0xF) {
            log_debug("\n");
        }

#endif

        //log_debug("read=0x%x 0x%x \n", boot_odo_km_v[0],boot_odo_km_v[1]);
        if(tmp == 0xFFFFFFFF) {
            //if(boot_odo_km_v[1] == 0xFFFFFFFF) {
            //log_debug("i:0x%x\n",i);
            break;
            //}
        }
    }

#ifdef DEBUG_BOOT_GET_FLASH
    log_debug("\n");

    if(i == (FLASH_PAGE_SIZE / 8)) {
        for(i = 0; i < (FLASH_PAGE_SIZE / 8); i++) {
            tmp = *(uint32_t*)(SAVE_ADDR + i * 8);
            tmp2 = *(uint32_t*)(SAVE_ADDR + i * 8 + 4);
            //log_debug("read=0x%x ", boot_odo_km_v[0]);
            //boot_odo_km_v[1] = *(uint32_t*)(SAVE_ADDR + (i + 1) * 4);
            log_debug("0x%X 0x%X ", tmp, tmp2);

            if((i & 0xF) == 0xF) {
                log_debug("\n");
            }
        }

        //while(1);
    }

    log_debug("get flash i: %d\n", i);
#endif

    if(i == 0) {
        if(tmp == 0xFFFFFFFF) {
            range_info.odo = 0;
            range_info.mul = 0;
            range_info.trip = 0;
            flash_info.trip = 0;
            flash_info.odo_dam = 0;
            flash_info.trip_dam = 0;
            flash_info.odo = 0;
            flash_info.mul = 0;
            flash_info.sel = 0;
        }

    } else {
        flash_info.odo = (*(uint32_t*)(SAVE_ADDR + (i - 1) * 8)) & 0xFFFF;
        flash_info.mul = (*(uint32_t*)(SAVE_ADDR + (i - 1) * 8) >> 16) & 0xFF;
        flash_info.sel = (*(uint32_t*)(SAVE_ADDR + (i - 1) * 8) >> 24) & 0xFF;
        flash_info.trip = (*(uint32_t*)(SAVE_ADDR + (i - 1) * 8 + 4)) & 0xFFFF;
        flash_info.trip_dam = ((*(uint32_t*)(SAVE_ADDR + (i - 1) * 8 + 4)) >> 24) & 0xFF;
        flash_info.odo_dam = ((*(uint32_t*)(SAVE_ADDR + (i - 1) * 8 + 4)) >> 16) & 0xFF;
        range_info.odo = flash_info.odo ;
        range_info.trip = flash_info.trip;
        range_info.mul = flash_info.mul;
    }

    //log_debug("\n\t\t\n\tis_lock_status=%d 0x%x 0x%x\n", is_lock_status, boot_odo_km_v[0], boot_odo_km_v[1]);
    i &= (FLASH_PAGE_SIZE / 8 - 1);
    flash_n = i;
#ifdef DEBUG_BOOT_GET_FLASH
    log_debug("boot n=0x%x %d %d %d %d %d %d\n", flash_n, flash_info.odo_dam, flash_info.odo, flash_info.mul, flash_info.trip_dam, flash_info.trip, flash_info.sel);
#endif
}



